# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Installs and configures Nova
"""

import os
import platform
import socket

from packstack.installer import basedefs
from packstack.installer import exceptions
from packstack.installer import processors
from packstack.installer import utils
from packstack.installer import validators

from packstack.modules import common
from packstack.modules.common import filtered_hosts
from packstack.modules.documentation import update_params_usage
from packstack.modules.ospluginutils import deliver_ssl_file
from packstack.modules.ospluginutils import generate_ssl_cert

# ------------- Nova Packstack Plugin Initialization --------------

PLUGIN_NAME = "OS-Nova"
PLUGIN_NAME_COLORED = utils.color_text(PLUGIN_NAME, 'blue')


def initConfig(controller):
    if platform.linux_distribution()[0] == "Fedora":
        primary_netif = "em1"
        secondary_netif = "em2"
    else:
        primary_netif = "eth0"
        secondary_netif = "eth1"

    nova_params = {
        "NOVA": [
            {"CMD_OPTION": 'nova-db-purge-enable',
             "PROMPT": (
                 "Enter y if cron job for removing soft deleted DB rows "
                 "should be created"
             ),
             "OPTION_LIST": ['y', 'n'],
             "VALIDATORS": [validators.validate_not_empty],
             "PROCESSORS": [processors.process_bool],
             "DEFAULT_VALUE": 'y',
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": False,
             "CONF_NAME": 'CONFIG_NOVA_DB_PURGE_ENABLE',
             "USE_DEFAULT": False,
             "NEED_CONFIRM": True,
             "CONDITION": False},

            {"CMD_OPTION": "nova-db-passwd",
             "PROMPT": "Enter the password for the Nova DB access",
             "OPTION_LIST": [],
             "VALIDATORS": [validators.validate_not_empty],
             "DEFAULT_VALUE": "PW_PLACEHOLDER",
             "PROCESSORS": [processors.process_password],
             "MASK_INPUT": True,
             "LOOSE_VALIDATION": False,
             "CONF_NAME": "CONFIG_NOVA_DB_PW",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": True,
             "CONDITION": False},

            {"CMD_OPTION": "nova-ks-passwd",
             "PROMPT": "Enter the password for the Nova Keystone access",
             "OPTION_LIST": [],
             "VALIDATORS": [validators.validate_not_empty],
             "DEFAULT_VALUE": "PW_PLACEHOLDER",
             "PROCESSORS": [processors.process_password],
             "MASK_INPUT": True,
             "LOOSE_VALIDATION": False,
             "CONF_NAME": "CONFIG_NOVA_KS_PW",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": True,
             "CONDITION": False},

            {"CMD_OPTION": "nova-manage-flavors",
             "PROMPT": (
                 "Should Packstack manage default Nova flavors"
             ),
             "OPTION_LIST": ["y", "n"],
             "VALIDATORS": [validators.validate_options],
             "DEFAULT_VALUE": "y",
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": False,
             "CONF_NAME": "CONFIG_NOVA_MANAGE_FLAVORS",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "novasched-cpu-allocation-ratio",
             "PROMPT": "Enter the CPU overcommitment ratio. Set to 1.0 to "
                       "disable CPU overcommitment",
             "OPTION_LIST": [],
             "VALIDATORS": [validators.validate_float],
             "DEFAULT_VALUE": 16.0,
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_SCHED_CPU_ALLOC_RATIO",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "novasched-ram-allocation-ratio",
             "PROMPT": ("Enter the RAM overcommitment ratio. Set to 1.0 to "
                        "disable RAM overcommitment"),
             "OPTION_LIST": [],
             "VALIDATORS": [validators.validate_float],
             "DEFAULT_VALUE": 1.5,
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_SCHED_RAM_ALLOC_RATIO",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "novacompute-migrate-protocol",
             "PROMPT": ("Enter protocol which will be used for instance "
                        "migration"),
             "OPTION_LIST": ['tcp', 'ssh'],
             "VALIDATORS": [validators.validate_options],
             "DEFAULT_VALUE": 'tcp',
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_COMPUTE_MIGRATE_PROTOCOL",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "nova-compute-manager",
             "PROMPT": ("Enter the compute manager for nova "
                        "migration"),
             "OPTION_LIST": [],
             "VALIDATORS": [validators.validate_not_empty],
             "DEFAULT_VALUE": "nova.compute.manager.ComputeManager",
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_COMPUTE_MANAGER",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "nova-ssl-cert",
             "PROMPT": ("Enter the path to a PEM encoded certificate to be used "
                        "on the https server, leave blank if one should be "
                        "generated, this certificate should not require "
                        "a passphrase"),
             "OPTION_LIST": [],
             "VALIDATORS": [],
             "DEFAULT_VALUE": '',
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_VNC_SSL_CERT",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "nova-ssl-key",
             "PROMPT": ("Enter the SSL keyfile corresponding to the certificate "
                        "if one was entered"),
             "OPTION_LIST": [],
             "VALIDATORS": [],
             "DEFAULT_VALUE": "",
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_VNC_SSL_KEY",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "nova-pci-alias",
             "PROMPT": ("Enter the PCI passthrough array of hash in JSON style for controller eg. "
                        "[{'vendor_id':'1234', 'product_id':'5678', "
                        "'name':'default'}, {...}] "),
             "OPTION_LIST": [],
             "VALIDATORS": [],
             "DEFAULT_VALUE": "",
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_PCI_ALIAS",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "nova-pci-passthrough-whitelist",
             "PROMPT": ("Enter the PCI passthrough whitelist as array of hash in JSON style for "
                        "controller eg. "
                        "[{'vendor_id':'1234', 'product_id':'5678', "
                        "'name':'default'}, {...}]"),
             "OPTION_LIST": [],
             "VALIDATORS": [],
             "DEFAULT_VALUE": "",
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_PCI_PASSTHROUGH_WHITELIST",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "nova-libvirt-virt-type",
             "PROMPT": (
                 "The nova hypervisor that should be used. Either qemu or kvm."
             ),
             "OPTION_LIST": ['qemu', 'kvm'],
             "DEFAULT_VALUE": '%{::default_hypervisor}',
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": False,
             "CONF_NAME": "CONFIG_NOVA_LIBVIRT_VIRT_TYPE",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": True,
             "CONDITION": False},
        ],

        "NOVA_NETWORK": [
            {"CMD_OPTION": "novacompute-privif",
             "PROMPT": ("Enter the Private interface for Flat DHCP on the Nova"
                        " compute servers"),
             "OPTION_LIST": [],
             "VALIDATORS": [],
             "DEFAULT_VALUE": '',
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_COMPUTE_PRIVIF",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "novanetwork-manager",
             "PROMPT": "Enter the Nova network manager",
             "OPTION_LIST": [r'^nova\.network\.manager\.\w+Manager$'],
             "VALIDATORS": [validators.validate_regexp],
             "DEFAULT_VALUE": "nova.network.manager.FlatDHCPManager",
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_NETWORK_MANAGER",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "novanetwork-pubif",
             "PROMPT": "Enter the Public interface on the Nova network server",
             "OPTION_LIST": [],
             "VALIDATORS": [validators.validate_not_empty],
             "DEFAULT_VALUE": primary_netif,
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_NETWORK_PUBIF",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "novanetwork-privif",
             "PROMPT": ("Enter the Private interface for network manager on "
                        "the Nova network server"),
             "OPTION_LIST": [],
             "VALIDATORS": [],
             "DEFAULT_VALUE": '',
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_NETWORK_PRIVIF",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "novanetwork-fixed-range",
             "PROMPT": "Enter the IP Range for network manager",
             "OPTION_LIST": ["^[\:\.\da-fA-f]+(\/\d+){0,1}$"],
             "PROCESSORS": [processors.process_cidr],
             "VALIDATORS": [validators.validate_regexp],
             "DEFAULT_VALUE": "192.168.32.0/22",
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_NETWORK_FIXEDRANGE",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "novanetwork-floating-range",
             "PROMPT": "Enter the IP Range for Floating IP's",
             "OPTION_LIST": ["^[\:\.\da-fA-f]+(\/\d+){0,1}$"],
             "PROCESSORS": [processors.process_cidr],
             "VALIDATORS": [validators.validate_regexp],
             "DEFAULT_VALUE": "10.3.4.0/22",
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_NETWORK_FLOATRANGE",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "novanetwork-auto-assign-floating-ip",
             "PROMPT": ("Should new instances automatically have a floating "
                        "IP assigned?"),
             "OPTION_LIST": ["y", "n"],
             "VALIDATORS": [validators.validate_options],
             "DEFAULT_VALUE": "n",
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": False,
             "CONF_NAME": "CONFIG_NOVA_NETWORK_AUTOASSIGNFLOATINGIP",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},
        ],

        "NOVA_NETWORK_VLAN": [
            {"CMD_OPTION": "novanetwork-vlan-start",
             "PROMPT": "Enter first VLAN for private networks",
             "OPTION_LIST": [],
             "VALIDATORS": [validators.validate_not_empty],
             "DEFAULT_VALUE": 100,
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_NETWORK_VLAN_START",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "novanetwork-num-networks",
             "PROMPT": "How many networks should be supported",
             "OPTION_LIST": [],
             "VALIDATORS": [validators.validate_not_empty],
             "DEFAULT_VALUE": 1,
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_NETWORK_NUMBER",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},

            {"CMD_OPTION": "novanetwork-network-size",
             "PROMPT": "How many addresses should be in each private subnet",
             "OPTION_LIST": [],
             "VALIDATORS": [validators.validate_not_empty],
             "DEFAULT_VALUE": 255,
             "MASK_INPUT": False,
             "LOOSE_VALIDATION": True,
             "CONF_NAME": "CONFIG_NOVA_NETWORK_SIZE",
             "USE_DEFAULT": False,
             "NEED_CONFIRM": False,
             "CONDITION": False},
        ],
    }
    update_params_usage(basedefs.PACKSTACK_DOC, nova_params)

    def use_nova_network(config):
        return (config['CONFIG_NOVA_INSTALL'] == 'y' and
                config['CONFIG_NEUTRON_INSTALL'] != 'y')

    def use_nova_network_vlan(config):
        manager = 'nova.network.manager.VlanManager'
        return (config['CONFIG_NOVA_INSTALL'] == 'y' and
                config['CONFIG_NEUTRON_INSTALL'] != 'y' and
                config['CONFIG_NOVA_NETWORK_MANAGER'] == manager)

    nova_groups = [
        {"GROUP_NAME": "NOVA",
         "DESCRIPTION": "Nova Options",
         "PRE_CONDITION": "CONFIG_NOVA_INSTALL",
         "PRE_CONDITION_MATCH": "y",
         "POST_CONDITION": False,
         "POST_CONDITION_MATCH": True},

        {"GROUP_NAME": "NOVA_NETWORK",
         "DESCRIPTION": "Nova Network Options",
         "PRE_CONDITION": use_nova_network,
         "PRE_CONDITION_MATCH": True,
         "POST_CONDITION": False,
         "POST_CONDITION_MATCH": True},

        {"GROUP_NAME": "NOVA_NETWORK_VLAN",
         "DESCRIPTION": "Nova Network VLAN Options",
         "PRE_CONDITION": use_nova_network_vlan,
         "PRE_CONDITION_MATCH": True,
         "POST_CONDITION": False,
         "POST_CONDITION_MATCH": True},
    ]
    for group in nova_groups:
        params = nova_params[group["GROUP_NAME"]]
        controller.addGroup(group, params)


def initSequences(controller):
    if controller.CONF['CONFIG_NOVA_INSTALL'] != 'y':
        return

    if controller.CONF['CONFIG_NEUTRON_INSTALL'] == 'y':
        network_title = ('Preparing OpenStack Network-related '
                         'Nova entries')
        network_function = create_neutron_manifest
    else:
        network_title = 'Preparing Nova Network entries'
        network_function = create_network_manifest

    novaapisteps = [
        {'title': 'Preparing Nova API entries',
         'functions': [create_api_manifest]},
        {'title': 'Creating ssh keys for Nova migration',
         'functions': [create_ssh_keys]},
        {'title': 'Gathering ssh host keys for Nova migration',
         'functions': [gather_host_keys]},
        {'title': 'Preparing Nova Compute entries',
         'functions': [create_compute_manifest]},
        {'title': 'Preparing Nova Scheduler entries',
         'functions': [create_sched_manifest]},
        {'title': 'Preparing Nova VNC Proxy entries',
         'functions': [create_vncproxy_manifest]},
        {'title': network_title,
         'functions': [network_function]},
        {'title': 'Preparing Nova Common entries',
         'functions': [create_common_manifest]},
    ]

    controller.addSequence("Installing OpenStack Nova API", [], [],
                           novaapisteps)


# ------------------------- helper functions -------------------------

def check_ifcfg(host, device):
    """
    Raises ScriptRuntimeError if given host does not have give device.
    """
    server = utils.ScriptRunner(host)
    cmd = "ip addr show dev %s || ( echo Device %s does not exist && exit 1 )"
    server.append(cmd % (device, device))
    server.execute()


def bring_up_ifcfg(host, device):
    """
    Brings given device up if it's down. Raises ScriptRuntimeError in case
    of failure.
    """
    server = utils.ScriptRunner(host)
    server.append('ip link show up | grep "%s"' % device)
    try:
        server.execute()
    except exceptions.ScriptRuntimeError:
        server.clear()
        cmd = 'ip link set dev %s up'
        server.append(cmd % device)
        try:
            server.execute()
        except exceptions.ScriptRuntimeError:
            msg = ('Failed to bring up network interface %s on host %s.'
                   ' Interface should be up so OpenStack can work'
                   ' properly.' % (device, host))
            raise exceptions.ScriptRuntimeError(msg)


def dummy_interface(host):
    """Creates dummy interface on given hosts.

    Returns interface name.
    """
    # Only single dummy interface will be created, hence the name is hardcoded
    ifname = 'dummy'
    script = (
        'DEVICE={0}\n'
        'BOOTPROTO=none\n'
        'ONBOOT=yes\n'
        'TYPE=Ethernet\n'
        'NM_CONTROLLED=no\n'.format(ifname)
    )
    server = utils.ScriptRunner(host)
    server.append(
        'ip link show {ifname} || ('
        'modprobe dummy && '
        'ip link set name {ifname} dev dummy0 && '
        'ip link set dev dummy address 06:66:DE:AF:66:60'
        ')'.format(**locals())
    )
    server.append(
        'cat > /etc/sysconfig/network-scripts/ifcfg-{ifname} '
        '<<EOF\n{script}EOF'.format(**locals())
    )
    server.execute()
    return ifname


# ------------------------ Step Functions -------------------------

def create_ssh_keys(config, messages):
    migration_key = os.path.join(basedefs.VAR_DIR, 'nova_migration_key')
    # Generate key if it does not exist
    if not os.path.exists(migration_key):
        local = utils.ScriptRunner()
        local.append('ssh-keygen -t rsa -b 2048 -f "%s" -N ""' % migration_key)
        local.execute()

    with open(migration_key) as fp:
        secret = fp.read().strip()
    with open('%s.pub' % migration_key) as fp:
        public = fp.read().strip()

    config['NOVA_MIGRATION_KEY_TYPE'] = 'ssh-rsa'
    config['NOVA_MIGRATION_KEY_PUBLIC'] = public.split()[1]
    config['NOVA_MIGRATION_KEY_SECRET'] = secret


def gather_host_keys(config, messages):
    global compute_hosts

    for host in compute_hosts:
        local = utils.ScriptRunner()
        local.append('ssh-keyscan %s' % host)
        retcode, hostkey = local.execute()
        config['HOST_KEYS_%s' % host] = hostkey


def create_api_manifest(config, messages):
    # Since this step is running first, let's create necessary variables here
    # and make them global
    global compute_hosts, network_hosts
    com_var = config.get("CONFIG_COMPUTE_HOSTS", "")
    compute_hosts = set([i.strip() for i in com_var.split(",") if i.strip()])
    net_var = config.get("CONFIG_NETWORK_HOSTS", "")
    network_hosts = set([i.strip() for i in net_var.split(",") if i.strip()])

    # This is a hack around us needing to generate the neutron metadata
    # password, but the nova puppet plugin uses the existence of that
    # password to determine whether or not to configure neutron metadata
    # proxy support. So the nova_api.pp template needs to be set to None
    # to disable metadata support if neutron is not being installed.
    if config['CONFIG_NEUTRON_INSTALL'] != 'y':
        config['CONFIG_NEUTRON_METADATA_PW_UNQUOTED'] = None
    else:
        config['CONFIG_NEUTRON_METADATA_PW_UNQUOTED'] = "%s" % config['CONFIG_NEUTRON_METADATA_PW']

    fw_details = dict()
    key = "nova_api"
    fw_details.setdefault(key, {})
    fw_details[key]['host'] = "ALL"
    fw_details[key]['service_name'] = "nova api"
    fw_details[key]['chain'] = "INPUT"
    fw_details[key]['ports'] = ['8773', '8774', '8775']
    fw_details[key]['proto'] = "tcp"
    config['FIREWALL_NOVA_API_RULES'] = fw_details


def create_compute_manifest(config, messages):
    global compute_hosts, network_hosts

    if config["CONFIG_HORIZON_SSL"] == 'y':
        config["CONFIG_VNCPROXY_PROTOCOL"] = "https"
    else:
        config["CONFIG_VNCPROXY_PROTOCOL"] = "http"

    migrate_protocol = config['CONFIG_NOVA_COMPUTE_MIGRATE_PROTOCOL']
    if migrate_protocol == 'ssh':
        config['CONFIG_NOVA_COMPUTE_MIGRATE_URL'] = (
            'qemu+ssh://nova@%s/system?no_verify=1&'
            'keyfile=/etc/nova/ssh/nova_migration_key'
        )
    else:
        config['CONFIG_NOVA_COMPUTE_MIGRATE_URL'] = (
            'qemu+tcp://nova@%s/system'
        )

    ssh_keys_details = {}
    for host in compute_hosts:
        try:
            hostname, aliases, addrs = socket.gethostbyaddr(host)
        except socket.herror:
            hostname, aliases, addrs = (host, [], [])

        for hostkey in config['HOST_KEYS_%s' % host].split('\n'):
            hostkey = hostkey.strip()
            if not hostkey:
                continue

            _, host_key_type, host_key_data = hostkey.split()
            key = "%s.%s" % (host_key_type, hostname)
            ssh_keys_details.setdefault(key, {})
            ssh_keys_details[key]['ensure'] = 'present'
            ssh_keys_details[key]['host_aliases'] = aliases + addrs
            ssh_keys_details[key]['key'] = host_key_data
            ssh_keys_details[key]['type'] = host_key_type

    config['SSH_KEYS'] = ssh_keys_details

    if config['CONFIG_VMWARE_BACKEND'] == 'y':
        vcenters = [i.strip() for i in
                    config['CONFIG_VCENTER_CLUSTER_NAMES'].split(',')
                    if i.strip()]
        if not vcenters:
            raise exceptions.ParamValidationError(
                "Please specify at least one VMware vCenter cluster in"
                " CONFIG_VCENTER_CLUSTER_NAMES"
            )
        if len(vcenters) != len(compute_hosts):
            if len(vcenters) > 1:
                raise exceptions.ParamValidationError(
                    "Number of vmware clusters %s is not same"
                    " as number of nova computes %s", (vcenters, compute_hosts)
                )
            else:
                vcenters = len(compute_hosts) * [vcenters[0]]
        vmware_clusters = dict(zip(compute_hosts, vcenters))
        config['CONFIG_VCENTER_CLUSTERS'] = vmware_clusters

    for host in compute_hosts:
        if config['CONFIG_IRONIC_INSTALL'] == 'y':
            cm = 'ironic.nova.compute.manager.ClusteredComputeManager'
            config['CONFIG_NOVA_COMPUTE_MANAGER'] = cm

        fw_details = dict()
        cf_fw_qemu_mig_key = "FIREWALL_NOVA_QEMU_MIG_RULES_%s" % host
        for c_host in compute_hosts:
            key = "nova_qemu_migration_%s_%s" % (host, c_host)
            fw_details.setdefault(key, {})
            fw_details[key]['host'] = "%s" % c_host
            fw_details[key]['service_name'] = "nova qemu migration"
            fw_details[key]['chain'] = "INPUT"
            fw_details[key]['ports'] = ['16509', '49152-49215']
            fw_details[key]['proto'] = "tcp"

        config[cf_fw_qemu_mig_key] = fw_details

        if config['CONFIG_NEUTRON_INSTALL'] != 'y':
            key = 'CONFIG_NOVA_COMPUTE_PRIVIF'
            if not config[key].strip():
                config[key] = dummy_interface(host)
            if config['CONFIG_USE_SUBNETS'] == 'y':
                netface = common.cidr_to_ifname(
                    config[key], host, config
                )
            else:
                netface = config[key]
            check_ifcfg(host, netface)
            try:
                bring_up_ifcfg(host, netface)
            except exceptions.ScriptRuntimeError as ex:
                # just warn user to do it by himself
                messages.append(str(ex))

        if config['CONFIG_CEILOMETER_INSTALL'] == 'y':
            if config['CONFIG_AMQP_ENABLE_SSL'] == 'y':
                ssl_cert_file = config['CONFIG_CEILOMETER_SSL_CERT'] = (
                    '/etc/pki/tls/certs/ssl_amqp_ceilometer.crt'
                )
                ssl_key_file = config['CONFIG_CEILOMETER_SSL_KEY'] = (
                    '/etc/pki/tls/private/ssl_amqp_ceilometer.key'
                )
                ssl_host = config['CONFIG_CONTROLLER_HOST']
                service = 'ceilometer'
                generate_ssl_cert(config, host, service, ssl_key_file,
                                  ssl_cert_file)

        fw_details = dict()
        key = "nova_compute"
        fw_details.setdefault(key, {})
        fw_details[key]['host'] = "%s" % config['CONFIG_CONTROLLER_HOST']
        fw_details[key]['service_name'] = "nova compute"
        fw_details[key]['chain'] = "INPUT"
        fw_details[key]['ports'] = ['5900-5999']
        fw_details[key]['proto'] = "tcp"
        config['FIREWALL_NOVA_COMPUTE_RULES'] = fw_details


def create_network_manifest(config, messages):
    global compute_hosts, network_hosts
    if config['CONFIG_NEUTRON_INSTALL'] == "y":
        return

    # set default values for VlanManager in case this values are not in config
    for key, value in [('CONFIG_NOVA_NETWORK_VLAN_START', 100),
                       ('CONFIG_NOVA_NETWORK_SIZE', 255),
                       ('CONFIG_NOVA_NETWORK_NUMBER', 1)]:
        config[key] = config.get(key, value)

    api_host = config['CONFIG_CONTROLLER_HOST']
    multihost = len(network_hosts) > 1
    config['CONFIG_NOVA_NETWORK_MULTIHOST'] = multihost and 'true' or 'false'
    for host in network_hosts:
        for i in ('CONFIG_NOVA_NETWORK_PRIVIF', 'CONFIG_NOVA_NETWORK_PUBIF'):
            if not config[i].strip():
                config[i] = dummy_interface(host)
            netface = config[i]
            if config['CONFIG_USE_SUBNETS'] == 'y':
                netface = common.cidr_to_ifname(netface, host, config)
            check_ifcfg(host, netface)
            try:
                bring_up_ifcfg(host, netface)
            except exceptions.ScriptRuntimeError as ex:
                # just warn user to do it by himself
                messages.append(str(ex))

        key = 'CONFIG_NOVA_NETWORK_AUTOASSIGNFLOATINGIP'
        config[key] = config[key] == "y"

        # We need to explicitly set the network size
        routing_prefix = config['CONFIG_NOVA_NETWORK_FIXEDRANGE'].split('/')[1]
        net_size = 2 ** (32 - int(routing_prefix))
        config['CONFIG_NOVA_NETWORK_FIXEDSIZE'] = str(net_size)


def create_sched_manifest(config, messages):
    if config['CONFIG_IRONIC_INSTALL'] == 'y':
        ram_alloc = '1.0'
        config['CONFIG_NOVA_SCHED_RAM_ALLOC_RATIO'] = ram_alloc


def create_vncproxy_manifest(config, messages):
    if config["CONFIG_HORIZON_SSL"] == 'y':
        if config["CONFIG_VNC_SSL_CERT"]:
            ssl_cert_file = config["CONFIG_VNC_SSL_CERT"]
            ssl_key_file = config["CONFIG_VNC_SSL_KEY"]
            if not os.path.exists(ssl_cert_file):
                raise exceptions.ParamValidationError(
                    "The file %s doesn't exist" % ssl_cert_file)

            if not os.path.exists(ssl_key_file):
                raise exceptions.ParamValidationError(
                    "The file %s doesn't exist" % ssl_key_file)

            final_cert = open(ssl_cert_file, 'rt').read()
            final_key = open(ssl_key_file, 'rt').read()
            deliver_ssl_file(final_cert, ssl_cert_file, config['CONFIG_CONTROLLER_HOST'])
            deliver_ssl_file(final_key, ssl_key_file, config['CONFIG_CONTROLLER_HOST'])

        else:
            config["CONFIG_VNC_SSL_CERT"] = '/etc/pki/tls/certs/ssl_vnc.crt'
            config["CONFIG_VNC_SSL_KEY"] = '/etc/pki/tls/private/ssl_vnc.key'
            ssl_key_file = config["CONFIG_VNC_SSL_KEY"]
            ssl_cert_file = config["CONFIG_VNC_SSL_CERT"]
            ssl_host = config['CONFIG_CONTROLLER_HOST']
            service = 'vnc'
            generate_ssl_cert(config, ssl_host, service, ssl_key_file,
                              ssl_cert_file)


def create_common_manifest(config, messages):
    global compute_hosts, network_hosts

    network_type = (config['CONFIG_NEUTRON_INSTALL'] == "y" and
                    'neutron' or 'nova')
    network_multi = len(network_hosts) > 1
    dbacces_hosts = set([config.get('CONFIG_CONTROLLER_HOST')])
    dbacces_hosts |= network_hosts

    for host in filtered_hosts(config):
        pw_in_sqlconn = False
        host = host.strip()

        if host in compute_hosts and host not in dbacces_hosts:
            # we should omit password in case we are installing only
            # nova-compute to the host
            perms = "nova"
            pw_in_sqlconn = False
        else:
            perms = "nova:%s" % config['CONFIG_NOVA_DB_PW']
            pw_in_sqlconn = True

        mariadb_host_url = config['CONFIG_MARIADB_HOST_URL']
        sqlconn = "mysql+pymysql://%s@%s/nova" % (perms, mariadb_host_url)
        if pw_in_sqlconn:
            config['CONFIG_NOVA_SQL_CONN_PW'] = sqlconn
        else:
            config['CONFIG_NOVA_SQL_CONN_NOPW'] = sqlconn

        # for nova-network in multihost mode each compute host is metadata
        # host otherwise we use api host
        if (network_type == 'nova' and network_multi and
                host in compute_hosts):
            metadata = host
        else:
            metadata = config['CONFIG_CONTROLLER_HOST']
        config['CONFIG_NOVA_METADATA_HOST'] = metadata

    if config['CONFIG_AMQP_ENABLE_SSL'] == 'y':
        nova_hosts = compute_hosts
        nova_hosts |= set([config.get('CONFIG_CONTROLLER_HOST')])
        ssl_cert_file = config['CONFIG_NOVA_SSL_CERT'] = (
            '/etc/pki/tls/certs/ssl_amqp_nova.crt'
        )
        ssl_key_file = config['CONFIG_NOVA_SSL_KEY'] = (
            '/etc/pki/tls/private/ssl_amqp_nova.key'
        )
        service = 'nova'
        for host in nova_hosts:
            generate_ssl_cert(config, host, service,
                              ssl_key_file, ssl_cert_file)


def create_neutron_manifest(config, messages):
    if config['CONFIG_NEUTRON_INSTALL'] != "y":
        return

    if config['CONFIG_IRONIC_INSTALL'] == 'y':
        virt_driver = 'nova.virt.firewall.NoopFirewallDriver'
        config['CONFIG_NOVA_LIBVIRT_VIF_DRIVER'] = virt_driver
    else:
        virt_driver = 'nova.virt.libvirt.vif.LibvirtGenericVIFDriver'
        config['CONFIG_NOVA_LIBVIRT_VIF_DRIVER'] = virt_driver
